Just in time for GBJAM8.
I had to remove the breakable pots to conserve tokens.
I am literally at 8192 tokens even after making even more optimizations!
I had to add the key and door to exit the stage.
Hey again. Goober here.
This revision has me REALLY looking in every little nook and cranny I can find
to get precious tokens. I've had to resort to utilizing SECRET SPECIAL tricks
to making sure I can load as much data as possible while not using very many
tokens at all. In the beginning of my code I've created a special function:
-- handy token-saving function glb =_𝘦𝘯𝘷 function vars(...) local a={...} local b=split(a[1],",") local c=2 for i in all(b) do glb[i]=a[c] c+=1 end end |
Pair this with one of my modules - the dialog module, for example:
vars("dlg_active,dlg_id,dlg_port,dlg_snd,dlg_tpos1,dlg_tpos2,dlg_text1,dlg_text2,dlg_next,dlg_pside,dlg_cblnk,dlg_done", false,nil,0, 0,0,0, "","",0, left,0,0 ) |
This will allow me to assign multiple global variables while saving about 1/3
of the tokens. A lot of data is being converted to strings in order to save
more and more tokens. Data which doesn't need to be written to very often
may also be stored together in strings, but I haven't really gotten to that
point yet.
What will PROBABLY happen is that I'll start looking into using peek/poke
for reading and writing data. One of the problems with using poke, though, is
that each call to this is 4 tokens. [poke ( offset , value]. The ending brace
doesn't count. Peeking is about the same. Even with shorthand peeking, it takes
four tokens to assign a value.
But, there are some features added to this version!
-
When attacking and killing bats, their spheara (currency is souls) is sucked
in by the magic lantern. When the lantern takes the soul, it glows bright for
a split second. -
It's now possible to die.
While it's not possible to be hurt by the bats
right now(fixed this), you can die from spikes or falling into the water at the bottom of
the stage. This results in a neat effect! Also, when you die, a password is
displayed. While this password doesn't serve any purpose right now, I'm planning
on allowing players to input their password upon startup. - You still can't end the level yet. The key actor has not yet been implemented.
Because I'm out of tokens right now, I'll need to save some more before I can
implement this. It's in the works!
I think that's it for now. See ya!
~ Goober
Hey again. Goober here.
I've made some impressive changes to the cart! Here's some of the neat things
I added:
-
You can attack and kill the bats and pots in the level! For now they both
add 10 to your score. It also makes this neat little explosion when you
defeat them, with a cool sound effect. -
The magic lantern in the cutscene now shows up in gameplay! This lantern
is the basis of storing your currency, which is called Spheara. It glows
faintly as it travels with you. The spirit of the hero's brother Coji also
resides in this lantern. -
You still can't die, but I'm working on it. If you're on the bottom level of
the map and you fall through the water below, you'll have to reset. -
There's still some oddities with the map looping and actors not really
behaving like it's a seamless loop, but at least the foreground and background
map tiles do! -
The map was simplified to make it easier to build levels via 8x8 chunks.
This does limit the amoutn of variety for now, but it's a fine placeholder
for building as many maps as I would like. - While not implemented, there exists a password system hidden in the game's
code which will be utilized at some point later in development.
Again, I'm topped off on tokens, which means I'm gonna have to find ways to
save some somewhere.
Tokens: 8190 / 8192
Characters: 43954 / 65535
Compression: 90%
Believe it or not, compression was actually hassling me too. I lessened this
by replacing the detailed map from a prior version to a chunk-based one. The
string size is MUCH less than the former.
While adding stages is absolutely no hassle at all (really only adds a handful
of tokens each level (like.. 9?) adding content for the game is proving to be
very difficult. I don't want to remove anything that is currently in place,
and I'm already scrubbing every nook and cranny I can find for any amount of
tokens I can save. I still have a lot I need to do as well:
- Implement the other 4 abilities (Water, Ice, Lightning, Wind Magic)
- Make it so Makeii can get hurt and die, and also get a game over.
- Implement charged up abilities
- Utilize an ability-changing menu when holding down and Z
- Make a shield appear when Makeii is holding down and X.
- Implement more enemies so I can make use of the sprites I made for them.
- Implement key + locked door mechanic so you can leave the level.
I really REALLY wish that we had the ability to test games even if they go
over the token/char limit. While it is possible to still test carts over the
compression rate of 100%, it would be much easier to first implement all
of the content I need to and then compress and optimize whatever I can. I may
need to resort to a minifier to get everything to fit in there. Identifiers
which are substituting for numbers could probably be optimized that way.
That's all for now!
So I was thinking about how charming it would be to make a password system for my current game in progress, but I'm not entirely sure where to start on it.
I guess the first thing I would need to do is determine which values need to be saved to the password, determine the ranges in which these values are valid, determine the base for each character in the password, and then encrypt the password which displays to the player when they want to quit the game.
Let me make a small hypothetical data set that needs to be saved in the password:
HP-LV (min 0, max 6, 3 bits) MP-LV (min 0, max 6, 3 bits) SCORE (min 0, max 99999999, saves in multiples of 100, so real range is 0-999999, which means 20 bits) STAGE (min 0, max 7, 3 bits - stage saves as 0-7) INV. (bitfield of 10 items, 15 bits (5 items have level-2 upgrades, one bit each)) TOTAL: 44 bits (move this up to 48 to increase non-possible pw combos) HHH MMM SSSSSSSSSSSSSSSSSSSS TTT IIIIIIIIIIIIIII 0000 Possible PW Character Set (base 32): ABCDE FGHIJ KLMNO PQRST UVWXY Z1234 56 48 bits / 6 bits per character = password length of 8. Possible password attempt: QQQQQQQQ (Solar Jetman fans, word up!) Q=16 per character: 010000 010000 010000 010000 010000 010000 010000 010000 per assignment HHH MMM SSSSSSSSSSSSSSSSSSSS TTT I2I2I2I2I2IIIII 0000 010 000 01000001000001000001 000 001000001000001 0000 This equates to: HP-MAX Lv.2 MP-MAX Lv.0 SCORE: 266305+00 STAGE: 0 INV.: 2 items obtained (level 0), 1 passive item ERR BITS: 0 (pass) |
So, I need to figure out how to encrypt this password to be harder to interpret, as well as utilize the error bits at the end to further keep players from typing in random passwords and getting "cheat passwords". Any ideas on how I can go about this?
------------------------------------------------------------------------------- Well, this version definitely had me scraping every little inefficiency I could think of to save tokens. There's still a lot that can be saved, but right now I'm pretty much fighting against the token limit to try and add more and more features. Stats: tokens: 7885 / 8192 chars: 48853 / 65535 compression: 97% To start, I had to start doing variable recycling for scenes. Because the dot operator counts as a token, it's more efficient to keep data on the global namespace than to utilize containers. Because of this, scenes are using generic variables like "sc_1" through "sc_7" at the moment. As a strangely positive consequence, my game runs much more efficiently and uses a lot less CPU. I was originally concerned about doing it this way, but it turns out that I have a lot of RAM to spare. I think PICO-8 allows up to 2048 KB? The game's debug stats seem to show that I'm only using 250 KB or something. I'm watching my code sort of merge into a less elegant but more efficient style. It's more efficient to use expressions than branches, but it's still good to use functions instead of GOTO branching. as far as I know. It doesn't save any tokens to GOTO branch if I recall. Because comments affect the compression percentage of the cart, most of them have been removed. I'm expecting the code to become less readable as time goes on, but I'll have a good amount of knowledge about the code I've written for each section. Anyway, in this version, the first (non-killable) enemy is seen! There's bats flying around the place now, but I have to fix the fact that they'll fly off the edge of the map because their position doesn't wrap around like the player's does. If I set their solid flag to true, I can ride on them! It was turned off in this version, however. There is another actor that was made, but it's indistinguishable from the foreground tiles it represents. I've added pots to the game, which eventually will be breakable in order to get stuff like healing items and game currency. They will respawn like enemies do when you change vertical layers, so it may be an efficient way to farm things you will need. You may also notice in this version that a very weird noise plays when you step on spikes or lava. This was me testing harmful tiles while making sure that they don't hurt the player if they're still standing on the ground. The noise still plays when you're standing NEXT to spikes though, so I have to fix that. So far I haven't needed to sacrifice any of the content I've made for the game so far. I've only needed to compress their information or made their modules more efficient. I'll need to find clever ways to beat the token limit, which is my current adversary in development. Maybe I can implement a tiny string parser which can ease the load? It will cost CPU to add an interpreter on top of an interpreter (Lua) but if it means saving valuable tokens, then I'll start working on one. Cheers! - Goober ------------------------------------------------------------------------------- |
Hey again. Some new things in this update! * Platforms work! It took a little bit of code to get them to work as expected, but now they function to impede downward motion for entities while encouraging non-opacity for upwards velocity. In short, they do what platforms do. * While there's no enemies or hurtful terrain to worry about, there is a full four-layer stage to explore! Each stage will be somewhere around this size. I'm barely beneath the compression cap (which I didn't know was a thing until tonight) which will cause me to have some serious issues with optimizing short-term. Platforms use sprite flag 6, or [& 64]. The use of sprite flags have made it much easier to handle map collision data by associating the sprite IDs with these attributes. While there's only 8 flags to work with, they can be used as categories for further fine tuning with actual sprite IDs. Sprite flag 0 always serves as solid collision detection on the map. * Stage layers are decompressed (foreground and background) from a cheap and simple compression method which simply states [tile][amt] in hex. For example, the first stage layer starts off with "1204 b108" which means from the topleft of the stage area, it first places four tiles of ID of 18 (blank transparent). It then places adjacent to it eight tiles of 177 (bricks). This compression method, while simple, cuts the level data down to 1/3 of the usual size, mostly. If there's lots of the same tiles banded together horizontally, the compression method works extremely well. Stage 1-4's BG layer uses a gradient which makes it looks like this when compressed: "13388738883889388a388b388c70" This covers the entire 56 by 8 tiles of the background (16 by 8 tiles are not saved because the first and last 16x8 tiles for both the foreground and background are exactly the same to give the illusion of the looping mechanic). * Current cart stats: 8103/8192 tokens 49955/65535 chars 98% compressed size As you can see, I've pretty much hit the limit on both tokens and compressed size. I didn't think that I would need to worry so much about the third one, but it looks like even comments add to this percentage. I could very well be at the limit of the cart's virtual space and will need to look for some compression methods. This is the fun part! There are a lot of things which could be simplified. * One thing I didn't think I would have is extra map space. I'm actually not using much of it at all. A lot of the space was going to be used for world slices and make maps out of those. It may be something to resort to in the future, though. I want to keep all the data on a single cart and not feel like I need to load anything from an external file. However, should I need to utilize multiple carts for this game (kind of like multiple CDs for games back in the day), it may be an option. That's all for now! - Goober |
Okay! Quite a few changes in this version. You can walk the player around on a flat solid terrain (the world collision isn't put in yet so the only solid tiles are the ones on the very bottom). I made sure to make the movement of the player feel smooth when jumping, yet tight when walking left or right. It kind of works like Megaman a little bit. Pressing Z will jump, and pressing X will attack with a little magic slice. The player, who's name is Makeii, is a spellsword who has the ability to change his weapon since it's made of mana. The game's weapon mechanics have been simplified a lot (only one item slot on the top now). Holding down X will charge Makeii up. When at full charge, he can spend an AP (the triangle meter) to use the spell in his item slot. There aren't any spells coded in at the moment, so all it does is reduce AP by one point. Because there aren't a lot of buttons to use to map to things, ducking is considered Makeii's defensive position. I'm planning to allow the two buttons to activate a shield or to use defensive magic after charging, allowing each spell to have two different uses. Still in the works. I'm at 7488/8192 tokens, though. I'm going to have to find a way to compress and save tokens as I build the game more, but the token cap isn't deterring me from this goal. With each wip version, things get shifted around, more code gets added, and code already added gets optimized to save tokens. It's expected to hit and exceed the token cap in the near future, so cutting out some stuff or making things a little more efficient will need to happen. I found out that a pair of parenthesis counts as a single token () and multiplying is also one token, so functions which do things like multiply a variable by a number seems to be fine. For example, I'm using a function called "TL(x)" which acts as a multiplier for a tile size, which all it does is return (x*8). Three tokens for the definition of this function, and two tokens everywhere else. I could instead use the literal pixel value, but I feel like that's going to happen during the late optimization phases. Finally, comments don't add to the token count, only the char count. This is good because a lot of variables have abbreviated names due to the small screen space working with pico-8. I've already tripped up on some of my own variable names which don't make a lot of sense without context. Cheers! - Goober |
Howdy again. Here to give you some updates on the game so far. I've managed to save a fair amount of frames by not worrying so much about keeping information in tables and moving data to the global namespace. What I mean by this is instead of something like "nested.table.variables" instead I go with "global_namespace_variable". Using the latter method will save me lots of tokens. I've managed to add content and I'm actually lower than my last count in the previous post (5931/8192). The intro is pretty much done, so now all that's left is to work on gameplay. Currently there's a test stage. You can't control the player just yet, but it's next on my todo list. Instead you can pan the camera across the stage. I was aiming for a huge overworld with a map you could check on, but I guess instead I'll make it simple and keep the game stage-based, which should help keep my token count within reason. Each stage can have multiple rooms of varying length. I may even see about making the rooms loop horizontally, since the game takes place in a tower. The test area is currently manually mapped out just for visual purposes. The parallax looks okay, except when moving slowly. In this test you can see that I had some trouble lining up the building interior with the foreground, but it really doesn't look all too good even if it is lined up right. I could probably do some coding magic to alter the background tiles to interior, or maybe I just won't be bothered to go to that extent. Oh, also, there's a secret if you hang around long enough on the "MAGUS AORA" title screen. :] |
------------------------------------------------------------------------------- Some new things in this WIP since the previous version: * Added compressed slide data and slide module. This can fill the playfield with a static slide. The slide data is kept as a hex string, so each char can hold two pixels (with values 0-3). The playfield is 112x64 pixels, meaning that each slide is 3584 characters long. Decompressing and drawing the slide once to the screen probably takes more than a whole tick, but I was expecting it to be a lot slower. Win win! That looks like this, for no one asking: aaaaaaaaaaaaaaaaa8255555556802aaaaaaaaaaaaaaaaaaaaaaaaaaa6a6a6a6a6a6a6a6a46aaaaa 56aa80a6a6a6a6a6a6a6a6a6a6a6a6a6aaaaaaaaaaaaaaaaa0aaa0029a00a82aaaaaaaaaaaaaaaaa aaaaaaaa666666666666666661aa855498550c26666666666666666666666666a9a9a9a9a9a9a9a9 87aa1aa6a6695309a9a9a9a9a9a9a9a9a9a9a9a966666666666666660fea2aaaaaaaa4c266666666 666aaaa66666666699999999999999981ffc6aaaaaaaad30999999999aa000a99999999956565656 565656503ffcfe00902abf3c165656566801a42a5656565699999999999999807ff1ff28120fff4d 09999999a06809099999999955555555555555406ff3fcaa0283ffcfd15555556180020955555555 5959595959595900aff3f0aaa2a8ffd3a419595962008209595959595555555555555401aba3f2bf fff8fff25055555562026309555555559595959500959406aaa7cbfffffe3ff401959595a30a6309 959500955555555408055406aa8a0c03fff00ffc555555556309830955540805555555400a80541a 90093cb0ffcb8ff855550055a30a0c29554002055555540002a0006941043c3cffc383f81554200a 8000382aa90002805555400000aa01500104bc3fffc390a805000a2a8555003aaa0000a055550000 002a00000000affffbff80a8800002aa2aaaa03eaa80002815400000000a80001406affffcfe40a8 400002bf03ffff3ffa800002000000000002a802a502affffffe00a4000002bf1000000ffa800000 000000000000a80aa401affffffa00240000aabc1afff94ffe800000000000000000280aa0002bfc a3e904240000aafc6bf3fa43fea000000000000000000000000006ffffa414240000abfcaffcfe90 fea800000000000000000000000000bffaa0142400002bfcbff0ff90ffa800000000000000000000 5000181aaa40102400002afcafc03f90ffe80000000000000000004140001bc00000002400002afc 6bc03e90ffea0000000000000000014942016fffa480001000002afc1af0fa43ffaa800000000000 000004254800bfffa4aa001000002afc02bfe903feaa80000000000000002825080003ffe9280010 00000aa0b0000003feaa8000000000000000a8290800000000000a00000002802c0000c3faaa0000 000000000000aa940a02aabffaaaaa0000000a0002ffff03eaa80000000000000000aa400282abff fffff8200000080e202aa802aa800000000000000000a50202802abfffffe820000008280b000202 aa000000000000000000802a002200abfc3aabe00000080002fff800a000000000000000000003ea 800fc00000aabf8200000803c0000030a00000000000000000000ffa8100ff002aaffe0280000aaa c3003c20200000001000010050000ffe81500bffffffc00a80055402c383fc282800000015000001 50003fff8055002aa800000ea00000028e0faa02080000002955000000003fffa05550000001503f a00000028a0aaaa02800aaa40aa900000000ffffe05555500055503fe80000008028002aa0005550 01a954000000ffffe05a95555469503ffa000000aaa00000000000000000000095403fffe05aa695 54aa543ffa002a9000000000000000000000000025400fffe06aaa9514aa583ffe00aa5000045500 000000000aaa5000000003ffe06aaa9404aaa80ffe80554000015000000000000aa56aa0000013ff e85aaa9404aaaa0fff000000000000002aaa8000295aaaa90000243ff85aaa55542aba0fff000000 00000002aaaaa50015aaa5550000a90ff85aaa55552aaec3fc000012aaa000015555550000000000 0000e940005aa955052aefc0c0000016aa50000055500000000000001540ea540056a554012aefc0 0500000555000000000000006aaa90015500fa900056a950000aeff0290000000000140000000000 569550000000fa900056a950004afffc290014000000000000000000a55000000003fe800056a950 0003fffc3a4000000000000000005550550002a90003fe800055aa500013ffff0e8000000a800000 000055400000aaaaa003fe8000556a500010ffff0e80001aaaaaaa940000000000056aaa940ffa00 00156a540054ffff028000156aaaa55aa800000000005695540ffa0100055a5400542bffc2a00005 55555aaaaa80000000000555000ffa014001555555542babc3a000005555555555400000aaa00000 000ff8015000555555552baac3a800000555554000000000aaaa00000000f8000000055501540aaa 83fa01400000000000000000aaaa9400400800000008001020000aaa83f000000000000540000000 a5555500000aa0000140bc03e8038aaa8001000000000015400002a8555555000000950001540bcf ea3802aa40a90000000000040000aaaa00000001002800000169400ffa0042a6429000008aa80000 002aaaa900000055402a940005aa9543e8554155000940015aaa80000aaaaaa5 * The code's been slightly overhauled in the backend. Some minor speed improvements and token saving. I'm really worried about hitting the max token amount because I'm already at 6020 / 8192. I'm nowhere near the character limit, so the slides mentioned above can hold lots of data while barely affecting the token limit. * Attempts to keep things as modular as possible is saving me from duplicating a lot of code for no reason. There's always going to be exceptions, though. * IF/ELSEIF/ELSE branches are slow when you use them frequently on every tick, so plans to swap those out for function references are in the works. * Sorry for no actual gameplay. I plan to use cheap point-in-rect collision detection for whole-pixel movement and not worry so much about subpixel stuff. Velocities for actors will still retain floating point precision, but actors will "inch" pixel by pixel for the amount they need to move, and stop moving if they happen to collide with a world tile. It's simple and effective when moving small distances. Since the screen's resolution is already pretty small, and things won't move faster than 2-4 pixels at a time, I believe this is a great route to take. * I want to maintain a constant 60 FPS throughout the whole experience, so there are going to be some corners I'll need to cut later on. The HTML5 version chugs pretty bad on my Android device when I'm using per-pixel shading (the diamond effect, additive strips on the main menu and intro, etc). This causes the drawing FPS to drop to 30 FPS while logic is still kept at 60, and it's pretty evident that this can cause some issues with areas of the screen that don't clear/update frequently. S'about it for now. ------------------------------------------------------------------------------- |
No gameplay yet, but the main menu and stuff looks
pretty much complete for now.
You can access the cheat menu with cheat code
"< > < > O X O X" when the "MR.GOOBER" screen comes up.
There's no gameplay once the intro cutscene and dialogs play.
I'm really superstitious about showing off my work to people prematurely
because it could chance me not finishing this, but I felt like doing it
because I thought it looked cool.
I went for a 4-color style game with changeable palettes in the options
menu. I want to not use the START menu because it breaks immersion.
I'm already halfway at the token limit so I'm going to need to find some
optimizations in the code later on, I'm sure. I'm already out of sound
space, and I was really hoping to make more music for the game.
Anyway, hope you all like it.
- Goober